package extract.knowledge.general;

import com.thinkaurelius.titan.core.TitanGraph;
import com.thinkaurelius.titan.core.TitanTransaction;
import common.model.graph.CommonSchema;
import common.model.graph.ConceptSchema;
import common.model.knowledge.BaseDoc;
import common.model.knowledge.BaseDoc.IDValuePair;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by Common on 8/1/16.
 */
@Component
public abstract class BaseFactory {
    private static final Logger logger = LoggerFactory.getLogger(BaseFactory.class);

    public   String id_key;
    public   String label_value;
    public   String node_type_key;
    public   String node_type_value;
    public   String has_name_key;
    public   String has_name_zh_key;
    public   String has_name_en_key;

    public final TitanGraph graph;
    public final GraphTraversalSource g;

    @Inject
    public BaseFactory(TitanGraph graph){
        this.graph = graph;
        this.g = graph.traversal();

        this.id_key = CommonSchema.Properties.has_id;
        this.node_type_key = CommonSchema.Properties.has_node_type;

        this.has_name_key = CommonSchema.Properties.has_name;
        this.has_name_zh_key = CommonSchema.Properties.has_name_zh;
        this.has_name_en_key = CommonSchema.Properties.has_name_en;

    }

    public Vertex getOrCreateVertex(Integer id){

        Vertex vertex = null;
        this.logger.debug("[BaseFactory] prepare to create or get vertex, label = {} , id = {} " ,this.label_value, id);

        GraphTraversal vertexes = this.g.V().has(this.id_key, id).hasLabel(this.label_value);
        if(vertexes.hasNext()) {
            vertex = (Vertex)vertexes.next();
            this.logger.debug("[BaseFactory] found vertex,   label = {} , id = {} " ,this.label_value, id);
        }
        else{
            vertex = this.g.addV(T.label, this.label_value, this.id_key, id).next();
            setVertexNodeType(vertex);
            this.logger.debug("[BaseFactory] created vertex, label = {} , id = {} " ,this.label_value, id);
        }
        return vertex;
    }

    public boolean setVertexNodeType(Vertex vertex ){
        if( !hasProperty(vertex, this.node_type_key) ){
            setStringProperty(vertex, this.node_type_key, this.node_type_value);
        }
        return true;
    }

    public boolean setStringProperty(Vertex vertex, String key, String value){
        if(value == null) value = "";
        if( !hasProperty(vertex, key) ){
            vertex.property(key, value);
        }
        else if(lessThanOldder(vertex , key, value)){
            vertex.property(key , value);
        }
        return true;
    }

    public String getStringProperty(Vertex vertex, String key){
        if( hasProperty(vertex, key) ) return (String)vertex.property(key).value();
        return null;
    }

    public boolean hasProperty(Vertex vertex, String key){
        Set<String> keys = vertex.keys();
        if(keys.contains(key)) return true;
        return false;
    }

    public boolean lessThanOldder(Vertex vertex, String key, String value){
        if( ((String)vertex.property(key).value()).length() < value.length() ) return true;
        return false;
    }

    public Integer getVertexID(Vertex v){
        return (Integer)(v.property(id_key).value());
    }

    public boolean setNameProperty(Vertex vertex, String name){
        if(name == null) name = "";
        setStringProperty(vertex, this.has_name_key, name);
        String name_zh = NodeParser.parse_string_zh(name);
        setStringProperty(vertex, this.has_name_zh_key, name_zh);
        String name_en = NodeParser.parse_string_en(name);
        setStringProperty(vertex, this.has_name_en_key, name_en);
        logger.debug("[setNameProperty] vertex = " + vertex.toString() + ", name_zh = " + name_zh + ", name_en = " + name_en);
        return true;
    }

    public String getNameZh(Vertex vertex){
        return vertex.value(CommonSchema.Properties.has_name_zh);
    }



    public boolean setMetaEdge(Vertex start, String label, Vertex end){
        if(start == null || end == null) return false;
        Edge edge = start.addEdge(label, end);
        if(edge != null) return true;
        return false;
    }

    public boolean setMultiMetaEdge(Vertex start, String edge_label, List<BaseDoc.IDValuePair> pairs){
        if(pairs == null) return false;
        TitanTransaction tx = this.graph.newTransaction();
        for(BaseDoc.IDValuePair pair : pairs){
            Vertex end = VetexGenerator.genVertex(this.graph, pair);
            setMetaEdge(start, edge_label, end);
        }
        tx.commit();
        return true;
    }


    public List<Vertex> getAllVertexes(){

        List<Vertex> concept_list = new ArrayList<>();

        GraphTraversal traversal = this.g.V().hasLabel(label_value);

        while(traversal.hasNext()){
            Vertex vertex = (Vertex)traversal.next();
            this.logger.debug(vertex.id() + " " + getVertexID(vertex) + " " + vertex.label() + " "
                    + getStringProperty(vertex, this.has_name_key ));
            concept_list.add(vertex);
        }

        return concept_list;
    }

    public List<Vertex> getVertexesByID(Integer id){

        List<Vertex> concept_list = new ArrayList<>();

        GraphTraversal traversal = this.g.V().has(this.id_key, id).hasLabel(this.label_value);

        while(traversal.hasNext()){
            Vertex vertex = (Vertex)traversal.next();
            this.logger.debug(vertex.id() + " " + getVertexID(vertex) + " " + vertex.label());
            concept_list.add(vertex);
        }
        return concept_list;

    }


    public void delAllVertexes(){
        GraphTraversal traversal = this.g.V().hasLabel(label_value);

        while(traversal.hasNext()){
            Vertex vertex = (Vertex)traversal.next();
            this.logger.debug(vertex.id() + " " + getVertexID(vertex) + " " + vertex.label());
            vertex.remove();
        }
    }

    public void delVertexesByID(Integer id){
        GraphTraversal traversal = this.g.V().has(this.id_key, id).hasLabel(this.label_value);

        while(traversal.hasNext()){
            Vertex vertex = (Vertex)traversal.next();
            this.logger.debug(vertex.id() + " " + getVertexID(vertex) + " " + vertex.label());
            vertex.remove();
        }
    }





}
